package cc.decat.escript.codegen;

import cc.decat.escript.object.ESStringObject;
import cc.decat.escript.runtime.GlobalContext;
import cc.decat.escript.runtime.Instruction;
import cc.decat.escript.runtime.Opcode;

import java.util.ArrayList;
import java.util.List;

public class FunctionGenerate {
    private FunctionGenerate parent;
    private final String name;
    private final List<Instruction> instructionList;
    private final BlockGenerate bodyBlockGenerate;
    private final List<FunctionGenerate> childFunctionGenerateList;
    private int indexOfParent;

    public FunctionGenerate(String name, BlockGenerate bodyBlockGenerate) {
        this.name = name;
        this.instructionList = new ArrayList<>();
        this.bodyBlockGenerate = bodyBlockGenerate;
        this.childFunctionGenerateList = new ArrayList<>();
    }

    public List<Instruction> getInstructionList() {
        return instructionList;
    }

    public BlockGenerate getBodyBlockGenerate() {
        return bodyBlockGenerate;
    }

    public List<FunctionGenerate> getChildFunctionGenerateList() {
        return childFunctionGenerateList;
    }

    public void printInstruction(GlobalContext globalContext, String parentName) {
        System.out.println(parentName + " -> " + this.name + ":");
        BlockGenerate blockGenerate = this.bodyBlockGenerate;
        for (int i = 0; i < this.instructionList.size(); i++) {
            Instruction instruction = this.instructionList.get(i);
            int line = i + 1;
            if(instruction.getOpcode() == Opcode.ENTRY_BLOCK) {
                blockGenerate = blockGenerate.getChildBlockGenerateList().get(instruction.getOperand());
                System.out.printf("line %d: \t%s %d (%s)\n", line, instruction.getOpcode(), instruction.getOperand(), blockGenerate.getType());
                continue;
            }
            if(instruction.getOpcode() == Opcode.LEAVE_BLOCK) {
                blockGenerate = blockGenerate.getParent();
                System.out.printf("line %d: \t%s\n", line, instruction.getOpcode());
                continue;
            }
            if(instruction.getOpcode() == Opcode.LOAD_CONST) {
                System.out.printf("line %d: \t%s %d (%s)\n", line, instruction.getOpcode(), instruction.getOperand(), (globalContext.getConstObjectList().get(instruction.getOperand()).str()).getStr());
                continue;
            }
            if(instruction.getOpcode() == Opcode.LOAD_LOCAL_VAR || instruction.getOpcode() == Opcode.STORE_LOCAL_VAR) {
                System.out.printf("line %d: \t%s %d (%s)\n", line, instruction.getOpcode(), instruction.getOperand(), blockGenerate.getSymbolTable().getLocalVarInfoList().get(instruction.getOperand()).getName());
                continue;
            }
            if(instruction.getOpcode() == Opcode.LOAD_UP_VAR || instruction.getOpcode() == Opcode.STORE_UP_VAR) {
                System.out.printf("line %d: \t%s %d (%s)\n", line, instruction.getOpcode(), instruction.getOperand(), blockGenerate.getSymbolTable().getUpVarInfoList().get(instruction.getOperand()).getName());
                continue;
            }
            if(instruction.getOperand() == -1) {
                System.out.printf("line %d: \t%s\n", line, instruction.getOpcode());
            } else {
                System.out.printf("line %d: \t%s %d\n", line, instruction.getOpcode(), instruction.getOperand());
            }
        }
        for(FunctionGenerate functionGenerate : childFunctionGenerateList) {
            functionGenerate.printInstruction(globalContext, parentName + " -> " + this.name);
        }
    }

    public void setParent(FunctionGenerate functionGenerate) {
        this.parent = functionGenerate;
    }

    public FunctionGenerate getParent() {
        return parent;
    }

    public void setIndexOfParent(int indexOfParent) {
        this.indexOfParent = indexOfParent;
    }

    public int getIndexOfParent() {
        return indexOfParent;
    }

    public String getName() {
        return name;
    }
}
